#include "storage/buf_ext.h"
#include "common/hashfn.h"


KPHCPciPageBuffCtx *gPciBufCtx = NULL;


//Default 8K * 1000
Size KPHCPciBufferSize()
{
    return (Size)1024 * 1024 * 1024 * 2;
}

void KPHCPciLruPushNoLock(KPHCPciLruList *lru, KPHCPciPageCtrl *item, KPHCCtrlState state)
{
    KPHCPciPageCtrl *temp = NULL;
    if (lru->count == 0) {
        item->lruNext = PCI_INVALID_ID;
        item->lruPrev = PCI_INVALID_ID;
        lru->first = item->ctrlId;
        lru->last = item->ctrlId;
        lru->count++;
        item->state = state;
        return ;
    }
    item->lruNext = lru->first;
    item->lruPrev = PCI_INVALID_ID;
    temp = PCI_GET_CTRL_BY_ID(gPciBufCtx, lru->first);
    temp->lruPrev = item->ctrlId;
    lru->first = item->ctrlId;
    lru->count++;
    item->state = state;
}

/*
 * KPHCPciBufInitCtx
 *
 * Init Pci buffer blocks
 */
void KPHCPciBufInitCtx()
{
    char *buf;
    bool found = false;
    Size buffer_size = KPHCPciBufferSize();
    char*  pciBufferBlocks =
            (char *)CACHELINEALIGN(ShmemInitStruct("PCI Buffer Blocks", buffer_size, &found));

    if (found) {
        return;
    }

    buf = pciBufferBlocks;
    memset((void*)buf, 0, sizeof(KPHCPciPageBuffCtx));

    gPciBufCtx = (KPHCPciPageBuffCtx *)(void *)buf;
    gPciBufCtx->maxCount = (uint32)((buffer_size - sizeof(KPHCPciPageBuffCtx)) /
        (sizeof(KPHCPciPageCtrl) + BLCKSZ + 3 * sizeof(KPHCPciHashBucket)));
    gPciBufCtx->ctrlBuf = (KPHCPciPageCtrl *)(void *)(buf + sizeof(KPHCPciPageBuffCtx));
    gPciBufCtx->pageBuf =
        ((char *)(gPciBufCtx->ctrlBuf)) + (Size)((Size)gPciBufCtx->maxCount * sizeof(KPHCPciPageCtrl));
    gPciBufCtx->hashtbl.buckets = (KPHCPciHashBucket *)(void *)(
        ((char *)(gPciBufCtx->pageBuf)) + (Size)((Size)gPciBufCtx->maxCount * BLCKSZ));

    for (uint32 i = 0; i < PCI_PART_LIST_NUM; i++) {

        LWLockInitialize(&gPciBufCtx->mainLru[i].lock, (int)LWTRANCHE_PCI_BUFFER_CONTENT);
        LWLockInitialize(&gPciBufCtx->freeLru[i].lock, (int)LWTRANCHE_PCI_BUFFER_CONTENT);
    }

    for (uint32 i = 1; i <= gPciBufCtx->maxCount; i++) {
        KPHCPciPageCtrl *ctrl = PCI_GET_CTRL_BY_ID(gPciBufCtx, i);
        memset((void*)ctrl, 0, sizeof(KPHCPciPageCtrl));
        ctrl->ctrlId = i;
        ctrl->pciPage = (KPHCCfsExtentHeader *)(void *)(gPciBufCtx->pageBuf + (Size)((Size)(i - 1) * BLCKSZ));
        LWLockInitialize(&ctrl->contentLock, (int)LWTRANCHE_PCI_BUFFER_CONTENT);

        KPHCPciLruPushNoLock(&gPciBufCtx->freeLru[ctrl->ctrlId % PCI_PART_LIST_NUM], ctrl, CTRL_STATE_FREE);
    }
    gPciBufCtx->hashtbl.hash = tag_hash;
    gPciBufCtx->hashtbl.match = memcmp;
    gPciBufCtx->hashtbl.bucketNum = 3 * gPciBufCtx->maxCount;
    for (uint32 i = 1; i <= gPciBufCtx->hashtbl.bucketNum; i++) {
        KPHCPciHashBucket *bucket = PCI_GET_BUCKET_BY_ID(gPciBufCtx, i);
        memset((void*)bucket, 0, sizeof(KPHCPciHashBucket));
        bucket->bckId = i;
        LWLockInitialize(&bucket->lock, (int)LWTRANCHE_PCI_BUFFER_CONTENT);
    }
}

